﻿using Serilog;
using Serilog.Events;
using Serilog.Sinks.PeriodicBatching;
using SqlSugar;
using Zhp.Common.Appsetting;
using Zhp.Common.Helper;
using Zhp.Entity.Monitor;

namespace Zhp.Api.Extensions
{
    public static class SerilogSetup
    {
        public static WebApplicationBuilder AddSerilogSetup(this WebApplicationBuilder builder)
        {
            // 初始化Serilog  
            var logger = new LoggerConfiguration();

            logger.Enrich.FromLogContext();
            logger.MinimumLevel.Information(); //日志记录起始等级
            //logger.ReadFrom.Configuration(builder.Configuration);
            //命名空间必须是全名
            logger.MinimumLevel.Override("Default", LogEventLevel.Warning);
            logger.MinimumLevel.Override("System", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Microsoft", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information);
            logger.MinimumLevel.Override("Hangfire.SqlServer.SqlServerObjectsInstaller", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Hangfire.Server.BackgroundServerProcess", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Hangfire.BackgroundJobServer", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Hangfire.Server.ServerWatchdog", LogEventLevel.Error);
            logger.MinimumLevel.Override("Elsa.Runtime.StartupRunner", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Elsa.Services.Triggers.TriggerIndexer", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Elsa.Services.Bookmarks.BookmarkIndexer", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Elsa.StartupTasks.ContinueRunningWorkflows", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Elsa.Providers.Workflows.DatabaseWorkflowProvider", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Rebus.Bus.RebusBus", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Rebus.Routing.TypeBased.TypeBasedRouter", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Rebus.Threading.TaskParallelLibrary.TplAsyncTask", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Quartz.Simpl.RAMJobStore", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Quartz.Impl.StdSchedulerFactory", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Quartz.Core.QuartzScheduler", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Quartz.Core.SchedulerSignalerImpl", LogEventLevel.Warning);
            logger.MinimumLevel.Override("Quartz.ContainerConfigurationProcessor", LogEventLevel.Warning);
            logger.MinimumLevel.Override("AspNetCoreRateLimit.IpRateLimitMiddleware", LogEventLevel.Warning);

            if (AppHelper.WebHostEnvironment.IsDevelopment() || AppHelper.WebHostEnvironment.IsStaging())
            {
                logger.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}");
            }
            var options = builder.Services.GetOptions<SysLogOptions>();

            if (options.ToFile.Enabled)
            {
                //文件日志分文件夹存储
                foreach (LogEventLevel logLevel in Enum.GetValues(typeof(LogEventLevel)))
                {
                    logger.WriteTo.Conditional((logEvent) => logEvent.Level == logLevel, (x) => x.File
                    (
                        Path.Combine(AppContext.BaseDirectory, "Logs", logLevel.ToString(), ".log"),
                        LogEventLevel.Information,
                        rollingInterval: RollingInterval.Day,
                        fileSizeLimitBytes: 10485760 //10MB
                    ));
                }
            }

            if (options.ToDb.Enabled)
            {
                logger.WriteToDb(builder);
            }

            Log.Logger = logger.CreateLogger();

            builder.Services.AddLogging(builder =>
            {
                builder.ClearProviders();
                builder.AddSerilog(dispose: true);
            });

            Log.Information("Serilog初始化成功");

            return builder;
        }

        public static LoggerConfiguration WriteToDb(this LoggerConfiguration loggerConfiguration, WebApplicationBuilder builder)
        {
            var db = builder.Services.BuildServiceProvider().GetService<ISqlSugarClient>();
            if (db == null)
            {
                throw new SqlSugarException("数据库连接服务未在日志之前注册");
            }

            var loggerToDbSink = new LoggerToDbSink(db);

            var batchingOptions = new PeriodicBatchingSinkOptions
            {
                BatchSizeLimit = 500,
                Period = TimeSpan.FromSeconds(1),
                EagerlyEmitFirstEvent = true,
                QueueLimit = 10000
            };

            var batchingSink = new PeriodicBatchingSink(loggerToDbSink, batchingOptions);

            return loggerConfiguration.WriteTo.Sink(batchingSink);//.Filter.ByIncludingOnly(logEvent => logEvent.Level == LogEventLevel.Error);
        }
    }

    public class LoggerToDbSink : IBatchedLogEventSink
    {
        private ISqlSugarClient _db { get; set; }
        public LoggerToDbSink(ISqlSugarClient db)
        {
            _db = db;
        }

        public async Task EmitBatchAsync(IEnumerable<LogEvent> batchList)
        {
            if (!batchList.Any())
            {
                return;
            }
            var logs = new List<Logs>();
            foreach (var item in batchList)
            {
                logs.Add(new Logs
                {
                    LogLevel = item.Level.ToString(),
                    TimeStamp = item.Timestamp.DateTime,
                    Message = item.RenderMessage(),
                    MessageTemplate = item.MessageTemplate.Text,
                    Properties = item.Properties.ToJson()
                });
            }
            await _db.Insertable(logs).ExecuteCommandAsync();
        }

        public Task OnEmptyBatchAsync()
        {
            return Task.CompletedTask;
        }
    }
}
